package com.easy.tool.utils.bean;

import com.easy.tool.anno.BeanDes;
import com.easy.tool.anno.MethodDes;
import com.easy.tool.entity.ReflexEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.Logger;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 高俊
 * @create 2022-03-2022/3/2-20:43
 */
@BeanDes(name = "反射工具类", des = "可以通过该工具类进行简易的反射操作")
public class ReflexUtils {

    private static Logger logger = Logger.getLogger(ReflexUtils.class);
    private static int maxArg = 5;

    /**
     * 获取所有public类型的成员变量
     * @param clazz class类型
     * @return HashMap  变量名：变量类型
     */
    @MethodDes(des = "获取所有public类型的成员变量",param = {"arg0-> clazz：指定对象Class"})
    public static Map<String, Object> allPublicVariable(Class<?> clazz){
        Field[] filds = clazz.getFields();
        return getVariableSon(filds);
    }
    /**
     * 获取所有public类型的成员变量
     * @param object object类型
     * @return HashMap  变量名：变量类型
     */
    @MethodDes(des = "获取所有public类型的成员变量",param = {"arg0-> object：object类型"})
    public static Map<String, Object> allPublicVariable(Object object){
        Field[] filds = object.getClass().getFields();
        return getVariableSon(filds);
    }


    /**
     * 获取所有成员变量
     * @param clazz
     * @return HashMap  变量名：变量类型
     */
    @MethodDes(des = "获取所有成员变量",param = {"arg0-> clazz：指定对象Class"})
    public static Map<String, Object> getAllVariable(Class<?> clazz){
        Field[] filds = clazz.getDeclaredFields();
        return getVariableSon(filds);
    }
    /**
     * 获取所有成员变量
     * @param object
     * @return HashMap  变量名：变量类型
     */
    @MethodDes(des = "取所有成员变量",param = {"arg0-> object：指定对象"})
    public static Map<String, Object> getAllVariable(Object object){
        Field[] filds = object.getClass().getDeclaredFields();
        return getVariableSon(filds);
    }



    private static Map<String, Object>  getVariableSon(Field[] filds){
        Map<String, Object> map = new HashMap<>();
        for (Field fild : filds) {
            map.put(fild.getName(),fild.getType().getSimpleName());
        }
        return map;
    }

    //===========================================================================================================


    /**
     * 通过field获取单个 private 的值
     * @param t
     * @param <T>
     * @return
     */
    @MethodDes(des = "通过field获取单个 private 的值",param = {"arg0-> t：指定对象","arg1-> fieldName：变量名称"})
    public static <T> Object getDeclaredField(T t,String fieldName){
        Object value = null;
        try {
            Class<?> clazz = t.getClass();
            Field field = declaredField(clazz, fieldName);
            value = field.get(t);
        }catch (Exception e){
            e.printStackTrace();
        }
        return value;
    }

    /**
     * 通过field 设置 单个 private 的值
     * @param t
     * @param <T>
     * @return
     */
    @MethodDes(des = "通过field 设置 单个 private 的值",param = {"arg0-> t：指定对象","arg1-> fieldName：变量名称","arg2-> value: 所需要设置的值"})
    public static <T> T setDeclaredField(T t,String fieldName,Object value){
        try {
            Class<?> clazz = t.getClass();
            Field field = declaredField(clazz, fieldName);
            field.set(t,value);
        }catch (Exception e){
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 通过field 设置 单个 private 的值
     * @param clazz
     * @param fieldName
     * @param value
     * @return
     */
    @MethodDes(des = "通过field 设置 单个 private 的值",param = {"arg0-> clazz：指定对象Class","arg1-> fieldName：变量名称","arg2-> value: 所需要设置的值"})
    public static <T> T setDeclaredField(Class<T> clazz,String fieldName,Object value){
        T t = null;
        try {
            t = clazz.newInstance();
            Field field = declaredField(clazz, fieldName);
            field.set(t,value);
        }catch (Exception e){
            e.printStackTrace();
        }
        return t;
    }

    //===========================================================================================================

    /**
     * 执行方法（多个参数，多个paramType）
     * @param object        指定方法的所在对象
     * @param methodName    指定方法名称
     * @param args          方法参数集合
     * @param types         方法参数类型
     * @return
     */
    @MethodDes(des = "执行方法"
            ,explain = "多个参数，多个paramType"
            ,param = {"arg0-> object：指定方法的所在对象","arg1-> methodName：指定方法名称","arg2-> args: 方法参数集合","arg3-> types: 方法参数类型"})
    public static Object invokeMethod(Object object, String methodName, List<Object> args, Class<?>... types){
        Class<?> clazz = object.getClass();
        Object value = null;
        try {
            Method method = clazz.getDeclaredMethod(methodName,types);
            method.setAccessible(true);
            value = argsHandler(args, object, method);
        } catch (NoSuchMethodException e) {
            logger.error("获取方法类型名称或类型有误");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            logger.error("目标对象有误");
            e.printStackTrace();
        }catch (IllegalArgumentException e){
            logger.error("目标方法参数有误");
            e.printStackTrace();
        }

        return value;
    }

    /**
     * 执行方法（单个参数，单个paramType）
     * @param object        指定方法的所在对象
     * @param methodName    指定方法名称
     * @param arg          方法参数集合
     * @param type         方法参数类型
     * @return
     */
    @MethodDes(des = "执行方法"
            ,explain = "单个参数，单个paramType"
            ,param = {"arg0-> object：","arg1-> methodName：","arg2-> arg: ","arg3-> type: "})
    public static Object invokeMethod(Object object, String methodName, Object arg, Class<?> type){
        Class<?> clazz = object.getClass();
        Object value = null;
        try {
            Method method = clazz.getDeclaredMethod(methodName,type);
            method.setAccessible(true);

            List<Object> list = new ArrayList<>();
            list.add(arg);
            value = argsHandler(list, object, method);
        } catch (NoSuchMethodException e) {
            logger.error("获取方法类型名称或类型有误");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            logger.error("目标对象有误");
            e.printStackTrace();
        }catch (IllegalArgumentException e){
            logger.error("目标方法参数有误");
            e.printStackTrace();
        }

        return value;
    }

    private void invokeMthodSon(){

    }

    /******************************************************************************************************************/

    /**
     * 执行方法
     * @param args      参数集合
     * @param object    目标对象
     * @param method    Method对象
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private static Object argsHandler(List<Object> args,Object object,Method method) throws InvocationTargetException, IllegalAccessException {
        ReflexEntity entity = new ReflexEntity();

        if (args != null){
            int size = args.size();
            // 不超过最大的五个参数
            if (size > maxArg){
                size = maxArg;
            }
            for (int i = 0; i < size; i++) {
                setDeclaredField(entity,"param" + (i+1),args.get(i));
            }
            if (args.size() == 1){
                return method.invoke(object, entity.getParam1());
            }else if (args.size() == 2){
                return method.invoke(object, entity.getParam1(),entity.getParam2());
            }else if (args.size() == 3){
                return method.invoke(object, entity.getParam1(),entity.getParam2(),entity.getParam3());
            }else if (args.size() == 4){
                return method.invoke(object, entity.getParam1(),entity.getParam2(),entity.getParam3(),entity.getParam4());
            }else if (args.size() == 5){
                return method.invoke(object, entity.getParam1(),entity.getParam2(),entity.getParam3(),entity.getParam4(),entity.getParam5());
            }else {
                return method.invoke(object);
            }
        }else {
            return method.invoke(object);
        }


    }




    /**
     * private公用方法
     * @param clazz
     * @param fieldName
     * @param <T>
     * @return
     * @throws Exception
     */
    private static <T> Field declaredField(Class<T> clazz,String fieldName) throws Exception{
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field;
    }


}
